﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using CSharpExt.Util;

namespace CSharpExt.IO
{

    // ======================================================================
    // FastBuffer
    // ======================================================================

    /// <summary>
    /// Struttura dati che implementa un buffer che si puo' estendere in base
    /// alle necessita' e che permette di essere acceduto contemporaneamente in
    /// lettura e scrittura.
    /// Inoltre permette di accedere direttamente al byte array interno.
    /// 
    /// La dimensione del buffer e' sempre una potenza di 2
    /// </summary>
    public class FastBuffer
    {
        #region Factory Methods

        private static Pool<FastBuffer> _pool = new Pool<FastBuffer>();


        public static FastBuffer Acquire(int capacity)
        {
            FastBuffer fb = _pool.Acquire();
            fb.Clear();
            fb.EnsureCapacity(capacity);
            return fb;
        }

        public static void Release(FastBuffer fb)
        {
            if (fb != null)
                _pool.Release(fb);
        }

        #endregion

        #region Private Data

        private static byte[] EMPTY_BUFFER = new byte[0];

        private byte[] _buffer; // buffer
        private int _capacity;  // == _buffer.Length

        private int _readAt;     // (lettura) posizione usata in lettura
        private int _readEnd;    // (lettura) prima posizione non valida per la lettura
        private int _writeAt;    // (scrittura) posizione in cui scrivere

        #endregion

        #region Constructor

        public FastBuffer()
        {
            _buffer = EMPTY_BUFFER;
            _capacity = 0;
            _readEnd = 0;
        }

        public FastBuffer(int capacity)
        {
            _capacity = 1;
            while (_capacity < capacity)
                _capacity <<= 1;

            _buffer = new byte[_capacity];
            _readEnd = 0;
        }

        #endregion

        #region Properties

        // -- Commons -------------------------------------------------------

        public byte[] Buffer
        {
            get { return _buffer; }
            set
            {
                _buffer = value;
                _capacity = _buffer.Length;
            }
        }

        public int Capacity
        {
            get { return _capacity; }
        }

        // -- Read ----------------------------------------------------------

        public int Position
        {
            get { return _readAt; }
        }

        public int Index
        {
            get { return _readAt; }
        }

        public int Rest
        {
            get { return _readEnd - _readAt; }
        }

        // -- Write ---------------------------------------------------------

        public int Length
        {
            get { return _writeAt; }
            set { _writeAt = value; }
        }

        #endregion

        #region Operations

        // -- Commons -------------------------------------------------------

        public void Release()
        {
            Release(this);
        }

        // -- Read ----------------------------------------------------------

        public void Reset()
        {
            _readAt = 0;
        }

        // -- Write ---------------------------------------------------------

        public void Clear()
        {
            _writeAt = 0;
        }

        public void ExpandCapacity()
        {
            ensureCapacity(2 * _buffer.Length);
        }

        public void EnsureCapacity(int capacity)
        {
            ensureCapacity(_writeAt + capacity);
        }

        #endregion

        #region Operations - Unsafe Write (not expand)

        public void Skip(int nbytes)
        {
            _writeAt += nbytes;
        }

        public void WriteByte(byte b)
        {
            //ensureCapacity(_writeAt + 1);

            _buffer[_writeAt++] = b;
        }

        public void WriteByte(int index, byte b)
        {
            _buffer[index] = b;
        }

        public void WriteShort(short i)
        {
            //ensureCapacity(_writeAt + 2);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void WriteShort(ushort i)
        {
            //ensureCapacity(_writeAt + 2);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void WriteShort(int index, ushort i)
        {
            _buffer[index++] = (byte)i; i >>= 8;
            _buffer[index] = (byte)i; ;
        }

        public void WriteInt(int i)
        {
            //ensureCapacity(_writeAt + 4);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void WriteInt(int index, int i)
        {
            //ensureCapacity(_writeAt + 4);

            _buffer[index++] = (byte)i; i >>= 8;
            _buffer[index++] = (byte)i; i >>= 8;
            _buffer[index++] = (byte)i; i >>= 8;
            _buffer[index] = (byte)i; ;
        }

        public void WriteInt(uint i)
        {
            //ensureCapacity(_writeAt + 4);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void WriteString(String s)
        {
            //ensureCapacity(_writeAt + 4 + s.Length);

            /*WriteInt(s.Length);
            foreach (char c in s)
            {
                WriteByte((byte)c);
            }*/

            int length = Encoding.ASCII.GetBytes(s, 0, s.Length, _buffer, _writeAt + 4);
            WriteInt(_writeAt, length);
            _writeAt += length + 4;
        }

        public void WriteBuffer(byte[] buffer, int index, int length)
        {
            //ensureCapacity(_writeAt + length);

            System.Buffer.BlockCopy(buffer, index, _buffer, _writeAt, length);
            _writeAt += length;
        }

        public void Append(byte[] buffer, int index, int length)
        {
            //ensureCapacity(_writeAt + length);

            System.Buffer.BlockCopy(buffer, index, _buffer, _writeAt, length);
            _writeAt += length;
        }

        #endregion

        #region Operations - Safe Write (expand if necessary)

        public void SafeWriteByte(byte b)
        {
            ensureCapacity(_writeAt + 1);

            _buffer[_writeAt++] = b;
        }

        public void SafeWriteByte(int index, byte b)
        {
            _buffer[index] = b;
        }

        public void SafeWriteShort(short i)
        {
            ensureCapacity(_writeAt + 2);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void SafeWriteShort(ushort i)
        {
            ensureCapacity(_writeAt + 2);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void SafeWriteShort(int index, ushort i)
        {
            _buffer[index++] = (byte)i; i >>= 8;
            _buffer[index] = (byte)i; ;
        }

        public void SafeWriteInt(int i)
        {
            ensureCapacity(_writeAt + 4);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void SafeWriteInt(uint i)
        {
            ensureCapacity(_writeAt + 4);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void SafeWriteString(String s)
        {
            ensureCapacity(_writeAt + 4 + s.Length);

            SafeWriteInt(s.Length);
            foreach (char c in s)
            {
                SafeWriteByte((byte)c);
            }
        }

        public void SafeWriteBuffer(byte[] buffer, int index, int length)
        {
            ensureCapacity(_writeAt + length);

            System.Buffer.BlockCopy(buffer, index, _buffer, _writeAt, length);
            _writeAt += length;
        }

        public void SafeAppend(byte[] buffer, int index, int length)
        {
            ensureCapacity(_writeAt + length);

            System.Buffer.BlockCopy(buffer, index, _buffer, _writeAt, length);
            _writeAt += length;
        }

        #endregion

        #region Operations - Read

        public byte   ReadByte()
        {
            return _buffer[_readAt++];
        }

        public short  ReadShort()
        {
            short s = (short)(_buffer[_readAt++] & 0xFF);
            s |= (short)((_buffer[_readAt++] & 0xFF) << 8);
            return s;
        }

        public int    ReadInt()
        {
            int s = _buffer[_readAt++] & 0xFF;
            s |= (_buffer[_readAt++] & 0xFF) << 8;
            s |= (_buffer[_readAt++] & 0xFF) << 16;
            s |= (_buffer[_readAt++] & 0xFF) << 24;
            return s;
        }

        public String ReadString()
        {
            int length = ReadInt();

            /*char[] buffer = new char[length];
            for (int i = 0; i < length; ++i)
            {
                buffer[i] = (char)ReadByte();
            }
            return new String(buffer);*/
            
            String s = Encoding.ASCII.GetString(_buffer, _readAt, length);
            _readAt += length;
            return s;
        }

        public void   ReadBuffer(byte[] buffer, int index, int length)
        {
            System.Buffer.BlockCopy(_buffer, _readAt, buffer, index, length);
            _readAt += length;
        }

        #endregion

        #region Implementation

        private void ensureCapacity(int capacity)
        {
            if (_capacity < capacity)
            {
                if (_capacity == 0) 
                    _capacity = 1;

                while (_capacity < capacity)
                    _capacity <<= 1;

                byte[] obuffer = _buffer;
                _buffer = new byte[_capacity];
                System.Buffer.BlockCopy(obuffer, 0, _buffer, 0, obuffer.Length);
            }
        }

        #endregion

        #region Special

        public String AsString()
        {
            return new string(Encoding.ASCII.GetChars(_buffer, 0, _writeAt));
        }

        #endregion
    }
    
    
    
    public class FastBufferOld
    {
        #region Private Data

        private byte[] _buffer; // buffer
        private int _capacity;  // == _buffer.Length

        private int _readAt;     // (lettura) posizione usata in lettura
        private int _readEnd;    // (lettura) prima posizione non valida per la lettura
        private int _writeAt;    // (scrittura) posizione in cui scrivere

        #endregion

        #region Constructor

        public FastBufferOld()
        {
            _buffer = new byte[4096];
            _capacity = _buffer.Length;
        }

        public FastBufferOld(byte[] buffer)
        {
            _buffer = buffer;
            _capacity = _buffer.Length;
        }

        public FastBufferOld(int capacity)
        {
            _capacity = 1;
            while (_capacity < capacity)
                _capacity <<= 1;

            _buffer = new byte[_capacity];
        }

        public FastBufferOld(byte[] buffer, int index, int length)
        {
            _buffer = buffer;
            _readAt = index;
            _readEnd = index + length;
            _capacity = buffer.Length;
        }

        #endregion

        #region Properties

        public byte[] Buffer
        {
            get { return _buffer; }
            set
            {
                _buffer = value;
                _capacity = _buffer.Length;
            }
        }

        public int Index
        {
            get { return _readAt; }
        }

        public int Rest
        {
            get { return _readEnd - _readAt; }
        }

        #endregion

        #region Interface

        public int Length
        {
            get
            {
                return _writeAt;
            }
            set
            {
                _writeAt = value;
            }
        }

        public int Position
        {
            get
            {
                return _readAt;
            }
        }

        public void Clear()
        {
            _writeAt = 0;
        }

        public void Reset()
        {
            _readAt = 0;
        }

        public void EnsureCapacity(int capacity)
        {
            if ((_capacity - _writeAt) >= capacity)
                return;

            ensureCapacity(_capacity + (capacity - (_capacity - _writeAt)));
        }

        #endregion

        #region Interface - Write

        public void WriteByte(byte b)
        {
            ensureCapacity(_writeAt + 1);

            _buffer[_writeAt++] = b;
        }

        public void WriteByte(int index, byte b)
        {
            _buffer[index] = b;
        }

        public void WriteShort(short i)
        {
            ensureCapacity(_writeAt + 2);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void WriteShort(ushort i)
        {
            ensureCapacity(_writeAt + 2);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void WriteShort(int index, ushort i)
        {
            _buffer[index++] = (byte)i; i >>= 8;
            _buffer[index] = (byte)i; ;
        }

        public void WriteInt(int i)
        {
            ensureCapacity(_writeAt + 4);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void WriteInt(uint i)
        {
            ensureCapacity(_writeAt + 4);

            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; i >>= 8;
            _buffer[_writeAt++] = (byte)i; ;
        }

        public void WriteString(String s)
        {
            ensureCapacity(_writeAt + 4 + s.Length);

            WriteInt(s.Length);
            foreach (char c in s)
            {
                WriteByte((byte)c);
            }
        }

        public void WriteBuffer(byte[] buffer, int index, int length)
        {
            ensureCapacity(_writeAt + length);

            System.Buffer.BlockCopy(buffer, index, _buffer, _writeAt, length);
            _writeAt += length;
        }

        public void Append(byte[] buffer, int index, int length)
        {
            ensureCapacity(_writeAt + length);

            System.Buffer.BlockCopy(buffer, index, _buffer, _writeAt, length);
            _writeAt += length;
        }

        #endregion

        #region Interface - Read

        public byte ReadByte()
        {
            return _buffer[_readAt++];
        }

        public short ReadShort()
        {
            short s = (short)(_buffer[_readAt++] & 0xFF);
            s |= (short)((_buffer[_readAt++] & 0xFF) << 8);
            return s;
        }

        public int ReadInt()
        {
            int s = _buffer[_readAt++] & 0xFF;
            s |= (_buffer[_readAt++] & 0xFF) << 8;
            s |= (_buffer[_readAt++] & 0xFF) << 16;
            s |= (_buffer[_readAt++] & 0xFF) << 24;
            return s;
        }

        public String ReadString()
        {
            int length = ReadInt();
            char[] buffer = new char[length];
            for (int i = 0; i < length; ++i)
            {
                buffer[i] = (char)ReadByte();
            }
            return new String(buffer);
        }

        public void ReadBuffer(byte[] buffer, int index, int length)
        {
            System.Buffer.BlockCopy(_buffer, _readAt, buffer, index, length);
            _readAt += length;
        }

        #endregion

        #region Implementation

        private void ensureCapacity(int capacity)
        {
            if (_capacity < capacity)
            {
                while (_capacity < capacity)
                    _capacity <<= 1;

                byte[] temp = _buffer;
                _buffer = new byte[_capacity];
                temp.CopyTo(_buffer, 0);
            }
        }

        #endregion

        #region Special

        public String AsString()
        {
            return new string(Encoding.ASCII.GetChars(_buffer, 0, _writeAt));
        }

        #endregion
    }

}
